/*
 * This file is part of JGAP.
 *
 * JGAP offers a dual license model containing the LGPL as well as the MPL.
 *
 * For licensing information please see the file license.txt included with JGAP
 * or have a look at the top of class org.jgap.Chromosome which representatively
 * includes the JGAP license policy applicable for any file delivered with JGAP.
 */
package mk.finki.chemistry.dice;

import static mk.finki.chemistry.dice.DiceGAConstants.CCl4_DENSITY;
import static mk.finki.chemistry.dice.DiceGAConstants.CCl4_DENSITY_WEIGHT;
import static mk.finki.chemistry.dice.DiceGAConstants.CCl4_ISOTHERMAL_COMPRESSIBILITY;
import static mk.finki.chemistry.dice.DiceGAConstants.CCl4_ISOTHERMAL_COMPRESSIBILITY_WEIGHT;
import static mk.finki.chemistry.dice.DiceGAConstants.CCl4_MOLAR_SPECIFIC_HEAT;
import static mk.finki.chemistry.dice.DiceGAConstants.CCl4_MOLAR_SPECIFIC_HEAT_WEIGHT;
import static mk.finki.chemistry.dice.DiceGAConstants.CCl4_THERMAL_EXPANSION;
import static mk.finki.chemistry.dice.DiceGAConstants.CCl4_THERMAL_EXPANSION_WEIGHT;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.jgap.FitnessFunction;
import org.jgap.Gene;
import org.jgap.IChromosome;

public class DiceGridFitnessFunction extends FitnessFunction {

	private static final long serialVersionUID = 4994597233398579464L;
	static int i = 0;

	public static final String BASE_FOLDER = "/home/dragan/dicegrid";
	public static final String DICE_FOLDER = BASE_FOLDER + "/dice_CCl4";
	public static final String DICE_WORK_FOLDER_BASE = BASE_FOLDER + "/dice_work_";
	public static final String CCL4_TXT = "CCl4.txt";

	public double evaluate(IChromosome chromosome) {
		return evaluate1(chromosome);
	}

	public double evaluate1(IChromosome chromosome) {
		Gene[] genes = chromosome.getGenes();

		final double chargeC = (Double) genes[0].getAllele();
		final double epsilonC = (Double) genes[1].getAllele();
		final double sigmaC = (Double) genes[2].getAllele();
		final double epsilonCl = (Double) genes[3].getAllele();
		final double sigmaCl = (Double) genes[4].getAllele();

		final double chargeCl = -chargeC / 4;

		System.out.println(String.format(
				"evaluate %d: chargeC=%f epsilonC=%f sigmaC=%f epsilonCl=%f sigmaCl=%f", ++i, chargeC,
				epsilonC, sigmaC, epsilonCl, sigmaCl));

		final String diceWorkFolder = DICE_WORK_FOLDER_BASE
				+ new SimpleDateFormat("dd:MM:yyyy_hh:mm:ss:SSS").format(new Date());

		final String results = diceWorkFolder + "/results.out";
		final String ccl4Txt = diceWorkFolder + "/" + CCL4_TXT;
		try {
			// copy default dice configuration folder to work folder
			exec(String.format("cp -r %s %s", DICE_FOLDER, diceWorkFolder));

			// replace ter.in values with the genes
			exec(String.format("sed -i s/q_Cl/%f/g %s", chargeCl, ccl4Txt));
			exec(String.format("sed -i s/e_Cl/%f/g %s", epsilonCl, ccl4Txt));
			exec(String.format("sed -i s/s_Cl/%f/g %s", sigmaCl, ccl4Txt));
			exec(String.format("sed -i s/q_C/%f/g %s", chargeC, ccl4Txt));
			exec(String.format("sed -i s/e_C/%f/g %s", epsilonC, ccl4Txt));
			exec(String.format("sed -i s/s_C/%f/g %s", sigmaC, ccl4Txt));

			// run dice

			// thermalization phase
			exec("./term.sh", diceWorkFolder);

			// simulation phase
			exec("./sim.sh", diceWorkFolder);

			try {
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}

			double[] parameters = new double[4];
			int readFileRetryCount = 0;
			boolean readOk = false;
			do {
				try {
					BufferedReader in = new BufferedReader(new FileReader(results));

					String str = null;

					System.out.println("\nFILE: " + results);

					for (int i = 0; i < 4; i++) {
						str = in.readLine();
						System.out.println("STRING:" + str);
						System.out.println("DOUBLE:" + Double.parseDouble(str));
						parameters[i] = Double.parseDouble(str);
					}
					readOk = true;
					in.close();
				} catch (Exception e) {
					e.printStackTrace();
					try {
						Thread.sleep(2000);
						readFileRetryCount++;
					} catch (InterruptedException e1) {
						e1.printStackTrace();
					}
				}
			} while (!readOk && readFileRetryCount <= 2);

			if (!readOk)
				return 0.0;

			final double density = parameters[0];
			final double thermalExpansion = parameters[1];
			final double isothermalCompressibility = parameters[2] / 101325;
			final double molarSpecificHeat = parameters[3] * 4184;

			Chromosome ch = (Chromosome) chromosome;
			ch.setDensity(density);
			ch.setThermalExpansion(thermalExpansion);
			ch.setMolarSpecificHeat(molarSpecificHeat);
			ch.setIsothermalCompressibility(isothermalCompressibility);

			final double densityRelativeError = relativeError(density, CCl4_DENSITY);

			final double thermalExpansionRelativeError = relativeError(thermalExpansion,
					CCl4_THERMAL_EXPANSION);

			final double isothermalCompressibilityRelativeError = relativeError(
					isothermalCompressibility, CCl4_ISOTHERMAL_COMPRESSIBILITY);

			final double molarSpecificHeatRelativeError = relativeError(molarSpecificHeat,
					CCl4_MOLAR_SPECIFIC_HEAT);

			final double fitness =

			densityRelativeError * CCl4_DENSITY_WEIGHT

			+ thermalExpansionRelativeError * CCl4_THERMAL_EXPANSION_WEIGHT

			+ isothermalCompressibilityRelativeError * CCl4_ISOTHERMAL_COMPRESSIBILITY_WEIGHT

			+ molarSpecificHeatRelativeError * CCl4_MOLAR_SPECIFIC_HEAT_WEIGHT;

			DecimalFormat sciFormat = new DecimalFormat("0.######E0");

			System.out.println(String.format("\nFitness = %f printing details:", fitness));

			System.out.println(String.format("Density = %s (%s) err: %f %s", sciFormat.format(density),
					sciFormat.format(CCl4_DENSITY), densityRelativeError * 100, "%"));

			System.out.println(String.format("Thermal expansion = %s (%s) err: %f %s",
					sciFormat.format(thermalExpansion), sciFormat.format(CCl4_THERMAL_EXPANSION),
					thermalExpansionRelativeError * 100, "%"));

			System.out.println(String.format("Isothermal compressibility = %s (%s) err: %f %s",
					isothermalCompressibility, CCl4_ISOTHERMAL_COMPRESSIBILITY,
					isothermalCompressibilityRelativeError * 100, "%"));

			System.out.println(String.format("Molar specific heat = %s (%s) err: %f %s",
					sciFormat.format(molarSpecificHeat), sciFormat.format(CCl4_MOLAR_SPECIFIC_HEAT),
					molarSpecificHeatRelativeError * 100, "%"));

			System.out.println();

			return 1000.0 / fitness;

		} catch (IOException ex) {
			ex.printStackTrace();
			return 0.0;
		}
	}

	private double relativeError(double measured, double real) {
		return Math.abs(measured - real) / real;
	}

	private static void exec(final String command) throws IOException {
		exec(command, null);
	}

	private static void exec(final String command, final String currentFolder) throws IOException {
		// System.out.println("Executing command: " + command);
		try {
			Process process = Runtime.getRuntime().exec(command, null,
					currentFolder != null ? new File(currentFolder) : null);
			process.waitFor();
			BufferedReader stdInput = new BufferedReader(new InputStreamReader(process.getInputStream()));

			BufferedReader stdError = new BufferedReader(new InputStreamReader(process.getErrorStream()));

			// read the output from the command
			boolean printIntro = false;
			String s = null;
			while ((s = stdInput.readLine()) != null) {
				if (!printIntro) {
					System.out.println("Here is the standard output of the command:");
					printIntro = true;
				}
				System.out.println(s);
			}

			// read any errors from the attempted command
			printIntro = false;
			while ((s = stdError.readLine()) != null) {
				if (!printIntro) {
					System.out.println("Here is the standard error of the command:");
					printIntro = true;
				}
				System.out.println(s);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) throws IOException {
		BufferedReader in = new BufferedReader(new FileReader(
				"/home/dragan/dicegrid/dice_work_19:04:2011_09:38:30/results.out"));

		String str = null;
		double[] parameters = new double[4];

		System.out.println("\nFILE: "
				+ "/home/dragan/dicegrid/dice_work_19:04:2011_09:38:30/results.out");

		for (int i = 0; i < 4; i++) {
			str = in.readLine();
			System.out.println("STRING:" + str);
			System.out.println("DOUBLE:" + Double.parseDouble(str));
			parameters[i] = Double.parseDouble(str);
		}

		in.close();
	}

	public double evaluate2(IChromosome chromosome) {

		int a = 1, b = 2;
		for (long i = 0; i < 500000000l; i++)
			a = a++ + b++;

		System.out.println("\nFITNESS 1\n");

		return 1;
	}
}
